You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We recently migrated our association code from rocket 0.4 to rocket 0.5.1. First there was no support for multipart data, and so we relied on rocket_multipart_form_data - crate, then we decided to use the TempFile data guard provided by rocket. We have a generic form upload for image, and for many format we support there is no valid ContentType, and because of that we decided to not specify it (given that it should be optional by our knowledge, and should not modify the parsing in any way).
But then we started getting malformed data out of our system.
I recreated a minimal reproducible to illustrate the issue.
Test Case
use reqwest::multipart;use rocket::{FromForm, form::Form, fs::TempFile, launch, post, routes};#[derive(FromForm)]structDummyForm<'a>{image:TempFile<'a>,}#[post("/", data = "<data>")]asyncfnsmall_post(mutdata:Form<DummyForm<'_>>) -> &'staticstr{//save file to a known location in order to read its content
data.image.persist_to("/tmp/recv.png").await.unwrap();let recv = std::fs::read("/tmp/recv.png").unwrap();//generate same datalet bytes:Vec<u8> = (0..140).map(|x| (x % 256)asu8).collect();//check if the data are the sameassert_eq!(bytes, recv);"ok"}#[launch]asyncfnrocket() -> _{// spawn a task to send a request
rocket::tokio::spawn(async{// wait for rocket startup
rocket::tokio::time::sleep(rocket::tokio::time::Duration::from_secs(1)).await;//generate bytes to sendlet bytes:Vec<u8> = (0..140).map(|x| (x % 256)asu8).collect();//build form ()let image = multipart::Part::bytes(bytes)//UNCOMMENT IF WANT TO SET CONTENT TYPE//.headers(HeaderMap::from_iter(vec![(CONTENT_TYPE, HeaderValue::from_static("image/png")),]));let form = multipart::Form::new().part("image", image);let client = reqwest::Client::builder().build().unwrap();let resp = client
.post("http://localhost:8000/").multipart(form).send().await.unwrap();println!("{:?}", resp);});
rocket::build().mount("/",routes![small_post])}
I haven't used the rocket local module because I do not know how to recreate the problem.
For the same reason I included the async module so that it is easily testable.
System Checks
My bug report relates to functionality.
I have tested against the latest Rocket release or a recent git commit.
I have tested against the latest stable rustc toolchain.
I was unable to find this issue previously reported.
The text was updated successfully, but these errors were encountered:
I improved the minimal example, so it is easier to follow and debug:
use rocket::{FromForm, form::Form, fs::TempFile, launch, post, routes};#[derive(FromForm)]structDummyForm<'a>{image:TempFile<'a>,}#[post("/", data = "<data>")]asyncfnsmall_post(mutdata:Form<DummyForm<'_>>) -> Vec<u8>{//save file to a known location in order to read its content
data.image.persist_to("/tmp/recv.png").await.unwrap();let recv = std::fs::read("/tmp/recv.png").unwrap();println!("Received {:?} bytes", recv);
recv
}#[launch]fnrocket() -> _{
rocket::build().mount("/",routes![small_post])}#[cfg(test)]mod test {usesuper::rocket;use rocket::http::ContentType;use rocket::http::Status;use rocket::local::blocking::Client;#[test]fnhello_world(){let client = Client::tracked(rocket()).expect("valid rocket instance");// Manually build a multipart bodylet boundary = "boundary123";let str:Vec<u8> = (0..256).map(|x| (x asu8)).collect();letmut body = Vec::new();
body.extend_from_slice(format!("--{boundary}\r\n\ Content-Disposition: form-data; name=\"image\"; filename=\"test.png\"\r\n").as_bytes(),);//UNCOMMENT ME IN ORDER TO USE THE CORRECT IMPLEMENTATION//body.extend_from_slice("Content-Type: image/png\r\n".as_bytes());
body.extend_from_slice("\r\n".as_bytes());
body.extend_from_slice(&str);
body.extend_from_slice(format!("\r\n--{boundary}--\r\n").as_bytes());println!("Body: {:?}",&body);let response = client
.post("/").header(ContentType::new("multipart","form-data").with_params([("boundary", boundary)]),).body(body).dispatch();assert_eq!(response.status(),Status::Ok);assert_eq!(response.into_bytes().unwrap(),str);}}
So what appens is that when content type is not specified, the form is considered a "value" and not a "data", this means that non standard ascii get's escaped (adding data). In theory this snouldn't be the correct implementation as stated by RFC_9110. So it should suffice to set it to data without any other problem right?
Rocket Version
0.5.1
Operating System
Arch Linux
Rust Toolchain Version
rustup 1.28.1 (2025-03-05)
What happened?
We recently migrated our association code from rocket 0.4 to rocket 0.5.1. First there was no support for multipart data, and so we relied on rocket_multipart_form_data - crate, then we decided to use the TempFile data guard provided by rocket. We have a generic form upload for image, and for many format we support there is no valid ContentType, and because of that we decided to not specify it (given that it should be optional by our knowledge, and should not modify the parsing in any way).
But then we started getting malformed data out of our system.
I recreated a minimal reproducible to illustrate the issue.
Test Case
Log Output
Additional Context
I haven't used the rocket local module because I do not know how to recreate the problem.
For the same reason I included the async module so that it is easily testable.
System Checks
rustc
toolchain.The text was updated successfully, but these errors were encountered: